/*
    Copyright Pat Niemeyer (pat@pat.net), 2007.
 */
package quickAdd.client;

import com.google.gwt.user.client.ui.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;

/**
 * The main spreadsheet display and entry panel.
 * This panel encapsulates knowledge of a worksheet and the ability to update
 * a view of a range of (context) cells at the bottom of the spreadsheet.
 *
 */
public class CellPanel extends VerticalPanel
    implements GData.RowPostedListener, GData.CellDataListener
{
    public static final int DEFAULT_CONTEXT_ROWS = 3;
    private Auth auth;
    private SimplePanel tableHolder = new SimplePanel();
    private FlexTable table;
    private List entryCells;
    private int numDataRows = DEFAULT_CONTEXT_ROWS;
    private Button addButton, refreshButton;
    private Label titleLabel;
    private GData.WorksheetMetadata DEFAULT_METADATA;
    private GData.WorksheetMetadata metadata;
    /** Used to determine of the WS row count has changed externally */
    private int expectedMaxWorksheetRow;

    public CellPanel( final Auth auth )
    {
        this.auth = auth;
        setStyleName( "quickadd-CellPanel" );
        setSpacing( 6 );

        DEFAULT_METADATA = createDefault();

        addButton = new Button( "Add Row" );
        addButton.addClickListener( new ClickListener() {
            public void onClick( Widget sender ) {
                GData.postRow( auth, metadata.columnNames, getEntryValues(),
                    metadata.worksheet.cellFeedURL, CellPanel.this );
            }
        } );

        refreshButton = new Button("Refresh");
        refreshButton.addClickListener( new ClickListener() {
            public void onClick( Widget sender ) {
                addStyleName( "quickadd-Wait" );
                updateCellData();
            }
        } );


        titleLabel = new Label();
        titleLabel.setStyleName( "quickadd-CellPanelLabel");
        add( titleLabel );
        add( tableHolder );

        HorizontalPanel p = new HorizontalPanel();
        p.setSpacing(6);
        p.add( addButton );
        p.add( refreshButton );
        add(p);

        configure( DEFAULT_METADATA );
    }

    public void configure ( GData.WorksheetMetadata metadata )
    {
        this.metadata = metadata;
        // Capture the initial row count, we'll maintain it after that and
        // incorporate external updates.
        this.expectedMaxWorksheetRow = metadata.rowCount;

        titleLabel.setText( metadata.worksheet.spreadsheet.title
            +": "+ metadata.worksheet.title );

        initTable();
        tableHolder.setWidget( table ); // replace

        if ( metadata == DEFAULT_METADATA ) {
            disableButtons();
        } else {
            enableButtons();
            updateCellData();
        }
    }


    private List getEntryValues()
    {
        List addValues = new ArrayList();
        for ( int i = 0; i < entryCells.size(); i++ ) {
            TextBox textBox = (TextBox)entryCells.get( i );
            addValues.add( textBox.getText() );
        }
        return addValues;
    }

    private void styleTableRow( int row ) {
        if ( row%2 == 0)
            table.getRowFormatter().setStyleName( row, "quickadd-CellTableRowAlt" );
        else
            table.getRowFormatter().setStyleName( row, "quickadd-CellTableRow" );
    }


    private void disableButtons() { setButtonState( false ); }
    private void enableButtons() { setButtonState( true ); }

    private void setButtonState( boolean enabled ) {
        addButton.setEnabled( enabled );
        refreshButton.setEnabled( enabled );
        String styleName =
            enabled ? "quickadd-ButtonEnabled" : "quickadd-ButtonDisabled";
        addButton.setStyleName( styleName );
        refreshButton.setStyleName( styleName );
    }

    // This is awkward, but decoupling this class from the WorksheetMetadata
    // is also painful.
    private static GData.WorksheetMetadata createDefault()
    {
        GData.Spreadsheet spreadsheet = new GData.Spreadsheet();
        spreadsheet.title = "Spreadsheet";

        GData.Worksheet worksheet = new GData.Worksheet();
        worksheet.spreadsheet = spreadsheet;
        worksheet.title = "Worksheet";

        GData.WorksheetMetadata wsMeta = new GData.WorksheetMetadata();
        wsMeta.worksheet = worksheet;
        wsMeta.columnNames =
            Arrays.asList( new String[] { "One", "Two", "Three" } );

        return wsMeta;
    }

    private void updateCellData() {
        int maxRow = expectedMaxWorksheetRow;
        if ( maxRow < 2 ) maxRow = 2;
        int minRow = maxRow - numDataRows +1;
        if ( minRow < 2 ) minRow = 2;
        GData.getCellData(
            auth, metadata.worksheet.cellFeedURL, minRow, maxRow, this );
    }

    public void clearEntryRow() {
        for ( int i = 0; i < entryCells.size(); i++ ) {
            TextBox textBox = (TextBox)entryCells.get( i );
            textBox.setText("");
        }
    }

    public void setDataCell( int row, int col, String value ) {
        table.setText( row+1, col, value );
    }

    // todo: move to its own class
    private void initTable()
    {
        table = new FlexTable();
        table.setStyleName( "quickadd-CellTable" );
        table.getRowFormatter().setStyleName( 0, "quickadd-CellHeader" );

        int numCols = metadata.columnNames.size();
        int row = 0;

        // header row
        for ( int i = 0; i < numCols; i++ ) {
            String value = (String)metadata.columnNames.get( i );
            table.setText( row, i, value );
        }
        row++;

        // data rows
        for (; row< numDataRows+1; row++) {
            for ( int col = 0; col < numCols; col++ ) {
                table.setText(row,col,"-");
                styleTableRow( row );
            }
        }

        entryCells = new ArrayList();
        // entry row
        for ( int i = 0; i < numCols; i++ ) {
            TextBox tb = new TextBox();
            tb.setStyleName( "quickadd-Cell");
            table.setWidget( row, i, tb );
            entryCells.add( tb );
        }
        styleTableRow( row );
    }

    public void rowBeginPosting() {
        QuickAdd.out( "adding row..." );
        addStyleName( "quickadd-Wait" );
        disableButtons();
    }

    public void rowPostedOk( List values )
    {
        // reset wait in cell data ok/error
        QuickAdd.out("Row posted ok!");
        expectedMaxWorksheetRow++;
        updateCellData();
        clearEntryRow();
        enableButtons();
    }
    public void rowPostedError()
    {
        QuickAdd.out("Row post error!");
        enableButtons();
        removeStyleName( "quickadd-Wait" );
    }

    public void setCellData( CellData cells, int actualWorksheetRowCount )
    {
        if ( actualWorksheetRowCount != expectedMaxWorksheetRow ) {
            QuickAdd.out("actual row count != expected: actual="+actualWorksheetRowCount+", "+ expectedMaxWorksheetRow );
            expectedMaxWorksheetRow = actualWorksheetRowCount;
            updateCellData(); // try again
            return;
        }
        // Assume that the CellData includes the correct dimensions
        for( int row=0; row<numDataRows; row++ ) {
            for( int col = 0; col< metadata.columnNames.size(); col++ ) {
                setDataCell( row, col, cells.getCellNormalized( row, col ) );
            }
        }
        removeStyleName( "quickadd-Wait" );
    }

    public void cellDataError()
    {
        QuickAdd.out( "cellDataError, resetting..." );
        // the most likely cause of this is that our max row count is wrong
        // (rows have stayed the same or shrunk)
        // so we pick the smallest possible and retry... the expected won't
        // match and we'll refesh a third time but be back in sync
        expectedMaxWorksheetRow = 1;
        // yes, there is potential for a loop here
        updateCellData();
        enableButtons();
    }

    public int getNumDataRows() { return numDataRows; }
    public void setNumDataRows( int numDataRows ) {
        this.numDataRows = numDataRows;
        configure( metadata );
    }

}
